package org.tru42.signal.model;

import java.lang.reflect.InvocationTargetException;

import org.tru42.signal.lang.ConnectException;
import org.tru42.signal.lang.ISignal;
import org.tru42.signal.lang.ISink;

public abstract class AbstractSink implements ISink {

	protected final String name;
	
	protected final Object owner;
	
	protected ISignal signal;
	
	public AbstractSink(Object owner, String name) {
		this.owner = owner;
		this.name = name;
	}
	
	@Override
	public void connectSignal(ISignal signal) throws ConnectException {
		if(!compatible(signal))
			throw new ConnectException("Incompatible signal");
		this.signal = signal;
	}
	
	public void disconnectSignal() {
		this.signal = null;
	}

	@Override
	public String getName() {
		return name;
	}

	@Override
	public Object getOwner() {
		return owner;
	}

	@Override
	public ISignal getSignal() {
		return signal;
	}

	@Override
	public abstract Class<?>[] getTypes();

	@Override
	public abstract void signalValueChanged(long timestamp, Object... args)
			throws IllegalArgumentException, IllegalAccessException,
			InvocationTargetException;
	
	@Override
	public boolean compatible(Object obj) {
		if(obj instanceof ISignal) {
			Class<?>[] localTypes = getTypes();
			Class<?>[] signalTypes = ((ISignal)obj).getTypes();
			if(signalTypes.length < localTypes.length)
				return false;
		
			for(int i=0; i<localTypes.length; i++)
				if( !(localTypes[i].isAssignableFrom(signalTypes[i])) )
					return false;
			return true; 
		}
		
		return false;
	}

	@Override
	public void connect(Object obj) throws ConnectException {
		try {
			connectSignal((ISignal) obj);
			signal.connectSink(this);
		} catch (Exception e) {
			throw new ConnectException(e.getMessage());
		}
	}
	
	@Override
	public void disconnect() {
		if(this.signal != null)
			this.signal.disconnectSink(this);
		this.signal = null;
	}

	@Override
	public boolean isConnected() {
		return signal != null;
	}
}
